﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using GeebaClient;
using System.Threading;
using ServerAdapter.WcfGeebaService;
using WcfGeebaServer.DBDAL;

namespace AcceptnaceTests
{
    [TestClass]
    public class GeneralClientTest
    {

        private static ConsoleClient_Accessor _target = new ConsoleClient_Accessor();
        private static Member _admin = null;

        public GeneralClientTest()
        {
            Data.ClearDB();
            User adminUser = _target.server.serverEntry();
            _admin = _target.server.serverLogin(adminUser, TestUtils.GetAdminUsername(), TestUtils.GetAdminPassword());
        }

        #region DurableTests

        [TestMethod()]
        [DeploymentItem("GeebaClient.exe")]
        public void DurableUsersTest()
        {
            string username = TestUtils.GetNextUsername();
            string password = TestUtils.GetPassword();
            User user = _target.server.serverEntry();
            _target.server.serverRegister(username, password);
            Member mem = null;

            for (int i = 0; i < 50; i++)
            {
                mem = _target.server.serverLogin(user, username, password);
                Assert.IsNotNull(mem);
                user = _target.server.serverLogout(mem);
                mem = null;
            }
        }

        [TestMethod()]
        [DeploymentItem("GeebaClient.exe")]
        public void DurableSubForumsTest()
        {
            string topic = AddNewSubForum();

            try
            {
                // attempt to find it over time...
                for (int i = 0; i < 50; i++)
                {
                    User user = _target.server.serverEntry();
                    bool found = false;
                    foreach (GSubForum sf in _target.server.serverGetSubForums())
                    {
                        if (sf.topic.Equals(topic))
                        {
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                    {
                        throw new Exception("Sub forum with topic " + topic + " was not found on the "+ i + "th iteration!");
                    }
                }
                Assert.IsTrue(true);
            }
            catch (Exception e)
            {
                Console.Write(e.Data);
                Assert.IsTrue(false);
            }
        }

        [TestMethod()]
        [DeploymentItem("GeebaClient.exe")]
        public void DurableThreads()
        {
            Data.ClearDB();
            string topic = AddNewSubForum();
            User olduser = RegisterNewMember();
            Member member = _target.server.serverLogin(olduser, TestUtils.GetCurrentUsername(), TestUtils.GetPassword());
            Assert.IsNotNull(member);
            GThread newMessage = PostNewThread(topic, member);
            Assert.IsTrue(newMessage._id >= 0);
            _target.server.serverLogout(member);

            User olduser2 = RegisterNewMember();
            // create sub forum stub
            GSubForum subforum = new GSubForum();
            subforum.topic = topic;

            // attempt to find it over time...
            for (int i = 0; i < 40; i++)
            {
                Member member2 = _target.server.serverLogin(olduser2, TestUtils.GetCurrentUsername(), TestUtils.GetPassword());
                // find thread in sub forum
                bool found = false;
                foreach (GThread th in _target.server.serverGetThreads(subforum))
                {
                    if (th._id.Equals(newMessage._id))
                    {
                        found = true;
                        break;
                    }
                }
                Assert.IsTrue(found);
                olduser2 = _target.server.serverLogout(member2);
            }
        }

        [TestMethod()]
        [DeploymentItem("GeebaClient.exe")]
        public void DurableComments()
        {
            Data.ClearDB();
            string topic = AddNewSubForum();
            User olduser = RegisterNewMember();
            Member member = _target.server.serverLogin(olduser, TestUtils.GetCurrentUsername(), TestUtils.GetPassword());
            Assert.IsNotNull(member);
            GThread newMessage = PostNewThread(topic, member);
            Assert.IsNotNull(newMessage);
            GPost post = PostNewComment(topic, member, newMessage);
            Assert.IsNotNull(post);
            Assert.IsNotNull(_target.server.serverLogout(member));

            User olduser2 = RegisterNewMember();
            Assert.IsNotNull(olduser2);
            // create sub forum stub
            GSubForum subforum = new GSubForum();
            subforum.topic = topic;

            // attempt to find it over time...
            for (int i = 0; i < 30; i++)
            {
                Member member2 = _target.server.serverLogin(olduser2, TestUtils.GetCurrentUsername(), TestUtils.GetPassword());
                Assert.IsNotNull(member2);
                // find thread in sub forum
                GThread thread = null;
                foreach (GThread th in _target.server.serverGetThreads(subforum))
                {
                    if (th._id.Equals(newMessage._id))
                    {
                        thread = th;
                        break;
                    }
                }
                Assert.IsNotNull(thread);

                // find post in thread
                bool found = false;
                foreach (GPost ps in _target.server.serverGetComments(member2, topic, thread._id))
                {
                    if (ps._id.Equals(ps._id))
                    {
                        found = true;
                        break;
                    }
                }
                Assert.IsTrue(found);

                olduser2 = _target.server.serverLogout(member2);
                Assert.IsNotNull(olduser2);
            }
        }

        #endregion

        #region EnduranceTest

        private static void EnduranceTestThread()
        {
            try
            {
                for (int i = 0; i < 5; i++)
                {
                    // enter
                    User user = _target.server.serverEntry();

                    // register
                    string username = TestUtils.GetNextUsername();
                    string password = TestUtils.GetPassword();
                    Assert.IsTrue(_target.server.serverRegister(username, password));

                    // login
                    Member member = _target.server.serverLogin(user, username, password);

                    // publish
                    GThread newMessage = new GThread();
                    newMessage._subject = TestUtils.GetNextSubject();
                    newMessage._content = TestUtils.GetNextContent();
                    newMessage._publisher = member;
                    newMessage._id = _target.server.serverPublish(member, TestUtils.GetCurrentTopic(), newMessage);

                    // watch it
                    GSubForum sf = new GSubForum();
                    sf.topic = TestUtils.GetCurrentTopic();
                    List<GThread> all_threads = _target.server.serverGetThreads(sf);
                    // verify threads
                    bool res = false;
                    for (int j = 0; j < all_threads.Count; j++)
                    {
                        if (all_threads[j]._id == newMessage._id)
                        {
                            res = true;
                            break;
                        }
                    }
                    if (!res)
                    {
                        throw new Exception("Could not find thread!");
                    }
                }
            }
            catch (Exception e)
            {
                Console.Write(e.Message);
            }
        }

        [TestMethod()]
        [DeploymentItem("GeebaClient.exe")]
        public void EnduranceTest()
        {
            List<Thread> allThreads = new List<Thread>();
            AddNewSubForum();
            for (int i = 0; i < 100; i++)
            {
                ConsoleClientTest consoleClient = new ConsoleClientTest();
                Thread consoleThread = new Thread(GeneralClientTest.EnduranceTestThread);
                allThreads.Add(consoleThread);
                consoleThread.Start();
            }
            foreach (Thread th in allThreads)
            {
                th.Join();
            }
            Assert.IsTrue(true);
        }

        #endregion

        #region InteractivityTests

        private static int _postedThread1 = 0;
        private static int _postedThread2 = 0;
        private static int _postedThread3 = 0;
        private static int _postedThread4 = 0;
        private static int _postedThread5 = 0;

        [TestMethod()]
        [DeploymentItem("GeebaClient.exe")]
        public void Interactivity()
        {
            ServerAdapter.SAdapter _server = _target.server;
            _target.listener.OnUpdateFromServer += new ServerAdapter.ClientListener.OnUpdate(listener_OnUpdateFromServerTest);
            string topic = AddNewSubForum();
            Member member = RegisterAndLoginNewMember();
            _server.serverSubscribe(member.username);
            PostNewThread(topic, member);
            
            Member member2 = RegisterAndLoginNewMember();
            _postedThread1 = PostNewThread(topic, member2)._id;
            
            string topic2 = AddNewSubForum();
            _postedThread2 = PostNewThread(topic2, member2)._id;

            _postedThread3 = PostNewThread(topic2, member)._id;
            _postedThread4 = PostNewThread(topic2, member2)._id;
            _postedThread5 = PostNewThread(topic2, member)._id;
        }

        void listener_OnUpdateFromServerTest(GThread message, DateTime timestamp, bool isNotification)
        {
            bool shouldNotify = (message._id == _postedThread1) || (message._id == _postedThread4);
            bool shouldNotNotify = (message._id == _postedThread2) || (message._id == _postedThread4) || (message._id == _postedThread5);
            if (isNotification)
            {
                if (shouldNotNotify)
                {
                    Assert.Fail();
                }
                if (shouldNotify)
                {
                    Assert.IsTrue(true);
                }
            }
            else
            {
                if (shouldNotify)
                {
                    // isNotification should be true!
                    Assert.Fail();
                }
            }
        }

        #endregion

        # region UtilMethods
        private static string AddNewSubForum()
        {
            string topic = TestUtils.GetNextTopic();
            _target.server.serverAddSubForum(_admin, topic, TestUtils.GetAdminUsername());
            return topic;
        }

        private static User RegisterNewMember()
        {
            User olduser = _target.server.serverEntry();
            string username = TestUtils.GetNextUsername();
            string password = TestUtils.GetPassword();
            _target.server.serverRegister(username, password);
            return olduser;
        }

        private static GThread PostNewThread(string topic, Member member)
        {
            GThread newMessage = new GThread();
            newMessage._subject = TestUtils.GetNextSubject();
            newMessage._content = TestUtils.GetContent();
            newMessage._publisher = member;
            newMessage._id = _target.server.serverPublish(member, topic, newMessage);
            return newMessage;
        }

        private static GPost PostNewComment(string topic, Member member, GThread newMessage)
        {
            GPost post = new GPost();
            post._subject = TestUtils.GetNextSubject();
            post._content = TestUtils.GetNextContent();
            post._publisher = member;
            post._id = _target.server.commentOnThread(member, post, topic, newMessage._id);
            return post;
        }

        private static Member LoginNewMemberWithCurrentName()
        {
            string username = TestUtils.GetCurrentMember();
            _target.server.serverRegister(username, TestUtils.GetPassword());
            Member mem = _target.server.serverLogin(_target.server.serverEntry(), username, TestUtils.GetPassword());
            return mem;
        }

        private static Member RegisterAndLoginNewMember()
        {
            RegisterNewMember();
            string username = TestUtils.GetCurrentUsername();
            _target.server.serverRegister(username, TestUtils.GetPassword());
            Member mem = _target.server.serverLogin(_target.server.serverEntry(), username, TestUtils.GetPassword());
            return mem;
        }

        private static bool DoesSubForumExist(string topic)
        {
            bool actual = false;
            foreach (GSubForum sf in _target.server.serverGetSubForums())
            {
                if (sf.topic.Equals(topic))
                {
                    actual = true;
                    break;
                }
            }
            return actual;
        }

        public static void AddSubForumWithCurrentTopic()
        {
            _target.server.serverAddSubForum(_admin, TestUtils.GetCurrentTopic(), TestUtils.GetAdminUsername());
        }

        public static void ConsoleAddThread()
        {
            AddSubForumWithCurrentTopic();
            Member mem = LoginNewMemberWithCurrentName();
            PostNewThread(TestUtils.GetCurrentTopic(), mem);
        }

        public static void ConsoleAddModerator()
        {
            AddSubForumWithCurrentTopic();
            Member member = LoginNewMemberWithCurrentName();

            // add new member as moderator
            for (int i = 0; i < 5; i++)
            {
                PostNewThread(TestUtils.GetCurrentTopic(), member);
            }
            _target.server.serverAddModerator(_admin, TestUtils.GetCurrentTopic(), member.username);
        }

        #endregion

    }
}
